#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>

#include <imp/imp_log.h>

#include "regrw.h"
#include "zkeygen.h"

#define TAG "Verify"

#define EFUSE_BASE		0x13540000
#define EFUSE_CTRL		(EFUSE_BASE + 0x0)
#define EFUSE_CFG		(EFUSE_BASE + 0x4)
#define EFUSE_STATUS	(EFUSE_BASE + 0x8)
#define EFUSE_DATA		(EFUSE_BASE + 0xc)


typedef unsigned int   uint32;
static uint32 gpio_base = 0;
static uint32 gpioz_base = 0;

#define GPIOB_BASE_PHY  0x10011000
#define GPIOZ_BASE_PHY  0x10017000

#define  PxINT(base)      (*(volatile uint32*)(base+0x010))
#define  PxMSK(base)      (*(volatile uint32*)(base+0x020))
#define  PxPAT1(base)     (*(volatile uint32*)(base+0x030))
#define  PxPAT0(base)     (*(volatile uint32*)(base+0x040))

#define PZINTC(base)    (*(volatile uint32*)(base+0x18))
#define PZMSKS(base)    (*(volatile uint32*)(base+0x24))
#define PZPAT1S(base)   (*(volatile uint32*)(base+0x34))
#define PZGID2LD(base)  (*(volatile uint32*)(base+0xF0))

typedef enum {
	SOC_T20,
	SOC_T30,
	SOC_T21,
	SOC_T31,
	SOC_T41,
	SOC_T23,
	SOC_T23ZN,
	SOC_T23DL,
	SOC_T23DN,
	SOC_T23ZX,
} IMPCPUID;

#define uswap_32(x) \
	((((x) & 0xff000000) >> 24) | \
	 (((x) & 0x00ff0000) >>  8) | \
	 (((x) & 0x0000ff00) <<  8) | \
	 (((x) & 0x000000ff) << 24))

#if __BYTE_ORDER == __LITTLE_ENDIAN
# define cpu_to_le32(x)		(x)
# define le32_to_cpu(x)		(x)
#else
# define cpu_to_le32(x)		uswap_32(x)
# define le32_to_cpu(x)		uswap_32(x)
#endif
#define tole(x) cpu_to_le32(x)

static const uint32_t crc_table[256] = {
	tole(0x00000000L), tole(0x77073096L), tole(0xee0e612cL), tole(0x990951baL),
	tole(0x076dc419L), tole(0x706af48fL), tole(0xe963a535L), tole(0x9e6495a3L),
	tole(0x0edb8832L), tole(0x79dcb8a4L), tole(0xe0d5e91eL), tole(0x97d2d988L),
	tole(0x09b64c2bL), tole(0x7eb17cbdL), tole(0xe7b82d07L), tole(0x90bf1d91L),
	tole(0x1db71064L), tole(0x6ab020f2L), tole(0xf3b97148L), tole(0x84be41deL),
	tole(0x1adad47dL), tole(0x6ddde4ebL), tole(0xf4d4b551L), tole(0x83d385c7L),
	tole(0x136c9856L), tole(0x646ba8c0L), tole(0xfd62f97aL), tole(0x8a65c9ecL),
	tole(0x14015c4fL), tole(0x63066cd9L), tole(0xfa0f3d63L), tole(0x8d080df5L),
	tole(0x3b6e20c8L), tole(0x4c69105eL), tole(0xd56041e4L), tole(0xa2677172L),
	tole(0x3c03e4d1L), tole(0x4b04d447L), tole(0xd20d85fdL), tole(0xa50ab56bL),
	tole(0x35b5a8faL), tole(0x42b2986cL), tole(0xdbbbc9d6L), tole(0xacbcf940L),
	tole(0x32d86ce3L), tole(0x45df5c75L), tole(0xdcd60dcfL), tole(0xabd13d59L),
	tole(0x26d930acL), tole(0x51de003aL), tole(0xc8d75180L), tole(0xbfd06116L),
	tole(0x21b4f4b5L), tole(0x56b3c423L), tole(0xcfba9599L), tole(0xb8bda50fL),
	tole(0x2802b89eL), tole(0x5f058808L), tole(0xc60cd9b2L), tole(0xb10be924L),
	tole(0x2f6f7c87L), tole(0x58684c11L), tole(0xc1611dabL), tole(0xb6662d3dL),
	tole(0x76dc4190L), tole(0x01db7106L), tole(0x98d220bcL), tole(0xefd5102aL),
	tole(0x71b18589L), tole(0x06b6b51fL), tole(0x9fbfe4a5L), tole(0xe8b8d433L),
	tole(0x7807c9a2L), tole(0x0f00f934L), tole(0x9609a88eL), tole(0xe10e9818L),
	tole(0x7f6a0dbbL), tole(0x086d3d2dL), tole(0x91646c97L), tole(0xe6635c01L),
	tole(0x6b6b51f4L), tole(0x1c6c6162L), tole(0x856530d8L), tole(0xf262004eL),
	tole(0x6c0695edL), tole(0x1b01a57bL), tole(0x8208f4c1L), tole(0xf50fc457L),
	tole(0x65b0d9c6L), tole(0x12b7e950L), tole(0x8bbeb8eaL), tole(0xfcb9887cL),
	tole(0x62dd1ddfL), tole(0x15da2d49L), tole(0x8cd37cf3L), tole(0xfbd44c65L),
	tole(0x4db26158L), tole(0x3ab551ceL), tole(0xa3bc0074L), tole(0xd4bb30e2L),
	tole(0x4adfa541L), tole(0x3dd895d7L), tole(0xa4d1c46dL), tole(0xd3d6f4fbL),
	tole(0x4369e96aL), tole(0x346ed9fcL), tole(0xad678846L), tole(0xda60b8d0L),
	tole(0x44042d73L), tole(0x33031de5L), tole(0xaa0a4c5fL), tole(0xdd0d7cc9L),
	tole(0x5005713cL), tole(0x270241aaL), tole(0xbe0b1010L), tole(0xc90c2086L),
	tole(0x5768b525L), tole(0x206f85b3L), tole(0xb966d409L), tole(0xce61e49fL),
	tole(0x5edef90eL), tole(0x29d9c998L), tole(0xb0d09822L), tole(0xc7d7a8b4L),
	tole(0x59b33d17L), tole(0x2eb40d81L), tole(0xb7bd5c3bL), tole(0xc0ba6cadL),
	tole(0xedb88320L), tole(0x9abfb3b6L), tole(0x03b6e20cL), tole(0x74b1d29aL),
	tole(0xead54739L), tole(0x9dd277afL), tole(0x04db2615L), tole(0x73dc1683L),
	tole(0xe3630b12L), tole(0x94643b84L), tole(0x0d6d6a3eL), tole(0x7a6a5aa8L),
	tole(0xe40ecf0bL), tole(0x9309ff9dL), tole(0x0a00ae27L), tole(0x7d079eb1L),
	tole(0xf00f9344L), tole(0x8708a3d2L), tole(0x1e01f268L), tole(0x6906c2feL),
	tole(0xf762575dL), tole(0x806567cbL), tole(0x196c3671L), tole(0x6e6b06e7L),
	tole(0xfed41b76L), tole(0x89d32be0L), tole(0x10da7a5aL), tole(0x67dd4accL),
	tole(0xf9b9df6fL), tole(0x8ebeeff9L), tole(0x17b7be43L), tole(0x60b08ed5L),
	tole(0xd6d6a3e8L), tole(0xa1d1937eL), tole(0x38d8c2c4L), tole(0x4fdff252L),
	tole(0xd1bb67f1L), tole(0xa6bc5767L), tole(0x3fb506ddL), tole(0x48b2364bL),
	tole(0xd80d2bdaL), tole(0xaf0a1b4cL), tole(0x36034af6L), tole(0x41047a60L),
	tole(0xdf60efc3L), tole(0xa867df55L), tole(0x316e8eefL), tole(0x4669be79L),
	tole(0xcb61b38cL), tole(0xbc66831aL), tole(0x256fd2a0L), tole(0x5268e236L),
	tole(0xcc0c7795L), tole(0xbb0b4703L), tole(0x220216b9L), tole(0x5505262fL),
	tole(0xc5ba3bbeL), tole(0xb2bd0b28L), tole(0x2bb45a92L), tole(0x5cb36a04L),
	tole(0xc2d7ffa7L), tole(0xb5d0cf31L), tole(0x2cd99e8bL), tole(0x5bdeae1dL),
	tole(0x9b64c2b0L), tole(0xec63f226L), tole(0x756aa39cL), tole(0x026d930aL),
	tole(0x9c0906a9L), tole(0xeb0e363fL), tole(0x72076785L), tole(0x05005713L),
	tole(0x95bf4a82L), tole(0xe2b87a14L), tole(0x7bb12baeL), tole(0x0cb61b38L),
	tole(0x92d28e9bL), tole(0xe5d5be0dL), tole(0x7cdcefb7L), tole(0x0bdbdf21L),
	tole(0x86d3d2d4L), tole(0xf1d4e242L), tole(0x68ddb3f8L), tole(0x1fda836eL),
	tole(0x81be16cdL), tole(0xf6b9265bL), tole(0x6fb077e1L), tole(0x18b74777L),
	tole(0x88085ae6L), tole(0xff0f6a70L), tole(0x66063bcaL), tole(0x11010b5cL),
	tole(0x8f659effL), tole(0xf862ae69L), tole(0x616bffd3L), tole(0x166ccf45L),
	tole(0xa00ae278L), tole(0xd70dd2eeL), tole(0x4e048354L), tole(0x3903b3c2L),
	tole(0xa7672661L), tole(0xd06016f7L), tole(0x4969474dL), tole(0x3e6e77dbL),
	tole(0xaed16a4aL), tole(0xd9d65adcL), tole(0x40df0b66L), tole(0x37d83bf0L),
	tole(0xa9bcae53L), tole(0xdebb9ec5L), tole(0x47b2cf7fL), tole(0x30b5ffe9L),
	tole(0xbdbdf21cL), tole(0xcabac28aL), tole(0x53b39330L), tole(0x24b4a3a6L),
	tole(0xbad03605L), tole(0xcdd70693L), tole(0x54de5729L), tole(0x23d967bfL),
	tole(0xb3667a2eL), tole(0xc4614ab8L), tole(0x5d681b02L), tole(0x2a6f2b94L),
	tole(0xb40bbe37L), tole(0xc30c8ea1L), tole(0x5a05df1bL), tole(0x2d02ef8dL)
};

/* ========================================================================= */
# if __BYTE_ORDER == __LITTLE_ENDIAN
#  define DO_CRC(x) crc = tab[(crc ^ (x)) & 255] ^ (crc >> 8)
# else
#  define DO_CRC(x) crc = tab[((crc >> 24) ^ (x)) & 255] ^ (crc << 8)
# endif

static uint32_t crc32_no_comp(uint32_t crc, const uint8_t *buf, int len)
{
    const uint32_t *tab = crc_table;
    const uint32_t *b =(const uint32_t *)buf;
    size_t rem_len;

    crc = cpu_to_le32(crc);
    /* Align it */
    if (((long)b) & 3 && len) {
	 uint8_t *p = (uint8_t *)b;
	 do {
	      DO_CRC(*p++);
	 } while ((--len) && ((long)p)&3);
	 b = (uint32_t *)p;
    }

    rem_len = len & 3;
    len = len >> 2;
    for (--b; len; --len) {
	 /* load data 32 bits wide, xor data 32 bits wide. */
	 crc ^= *++b; /* use pre increment for speed */
	 DO_CRC(0);
	 DO_CRC(0);
	 DO_CRC(0);
	 DO_CRC(0);
    }
    len = rem_len;
    /* And the last few bytes */
    if (len) {
	 uint8_t *p = (uint8_t *)(b + 1) - 1;
	 do {
	      DO_CRC(*++p); /* use pre increment for speed */
	 } while (--len);
    }

    return le32_to_cpu(crc);
}

static uint32_t crc32 (uint32_t crc, const uint8_t *p, int len)
{
     return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL;
}


static IMPCPUID zrt_get_cpu_id(void)
{
	int ret;
	static uint32_t  soc_id = 0xffffffff;

	if (soc_id == 0xffffffff) {
		ret = read_reg_32(0x13540238, &soc_id);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s:%d get_cpuinfo error\n", __func__, __LINE__);
			return -1;
		}
	}
    switch(((soc_id & 0xffff0000) >> 16)){
        case 0x3333:
            return SOC_T23DL;
        case 0x5555:
            return SOC_T23DN;
        case 0x7777:
            return SOC_T23ZN;
        case 0x6666:
            return SOC_T23ZX;
        default:
            IMP_LOG_ERR(TAG, "%s:%d get_cpuinfo id error\n", __func__, __LINE__);
        break;
    }
	return -1;
}


static int er_t20(uint32_t offset, uint32_t count, uint8_t *buf)
{
	int i;
	for (i = 0; i < count; i++) {
		uint32_t reg_tmp, timeout = 0x1000;
		do {
			if (read_reg_32(EFUSE_CTRL, &reg_tmp))
				return -1;
			timeout--;
		} while ((reg_tmp & 1) && timeout);

		if (timeout == 0)
			return -1;

		write_reg_32(EFUSE_CTRL, (offset << 21) | 1);

		timeout = 0x1000;
		do {
			read_reg_32(EFUSE_STATUS, &reg_tmp);
		} while (!(reg_tmp & 1) && timeout);

		read_reg_32(EFUSE_DATA, &reg_tmp);
		*buf = (uint8_t)reg_tmp;

		write_reg_32(EFUSE_STATUS, 0);

		buf++;
		offset++;
	}

	return 0;
}

static int er_t23(uint32_t offset, uint32_t count, uint32_t *buf)
{
	int i;
	for (i = 0; i < count; i++) {
		uint32_t reg_tmp, timeout = 0x1000;
		do {
			if (read_reg_32(EFUSE_CTRL, &reg_tmp))
				return -1;
			timeout--;
		} while ((reg_tmp & 1) && timeout);

		if (timeout == 0)
			return -1;

		write_reg_32(EFUSE_CTRL, (offset << 21) | 1);

		timeout = 0x1000;
		do {
			read_reg_32(EFUSE_STATUS, &reg_tmp);
		} while (!(reg_tmp & 1) && timeout);

		read_reg_32(EFUSE_DATA, &reg_tmp);
		*buf = reg_tmp;

		write_reg_32(EFUSE_STATUS, 0);

		buf++;
		offset++;
	}

	return 0;
}

#define CHIPID_BYTE_OFFSET 0
#define CHIPID_SIZE 12
#define ZKEY_BYTE_OFFSET 52
#define ZKEY_SIZE 4
#define DKEY_BYTE_OFFSET 50
#define DKEY_SIZE 1

static int set_gpio_input()
{
	/*to do*/
	static int memfd = 0;
	if ((memfd = open("/dev/mem", O_RDWR | O_SYNC)) < 0)
    {
		IMP_LOG_ERR(TAG, "open /dev/mem failed\n");
        return -1;
    }
	gpio_base = (uint32)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, GPIOB_BASE_PHY);
    if (gpio_base == (uint32)MAP_FAILED)
    {
		IMP_LOG_ERR(TAG, "mmap failed\n");
        close(memfd);
    }

	gpioz_base = (uint32)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, GPIOZ_BASE_PHY);
    if (gpioz_base == (uint32)MAP_FAILED)
    {
        IMP_LOG_ERR(TAG, "mmap z failed\n");
        close(memfd);
    }

	/* limit msc0 */
	/* msc0:PB00 PB01 PB02 PB03 PB04 PB05 */

	/* limit msc1 and Gmac */
	/* msc1:PB08 PB09 PB10 PB11 PB13 PB14 */
	/* Gmac:PB06 PB07 PB08 PB09 PB10 PB11 PB13 PB14 */

	/* limit UART0 */
	/* UART0:PB20 PB21 */

	/* limit UART2流控信号 */
	/* I2C1:PB25 PB26 */
	for(int i = 0; i < 27; i++){
		if((i == 12) || (i > 14 && i < 20) || (i > 21 && i < 25))
			continue;;
		uint32 pin_mask = 1 << i;
		uint32 pxint = PxINT(gpio_base);
		uint32 pxmsk = PxMSK(gpio_base);
		uint32 pxpat1 = PxPAT1(gpio_base);
		uint32 pxpat0 = PxPAT0(gpio_base);
		if (pxmsk & pin_mask)
        {
            if (pxpat1 & pin_mask)
            {
                // printf("gpio status is input\n");
            }
			else{
				PZINTC(gpioz_base) = pin_mask;
				PZMSKS(gpioz_base) = pin_mask;
				PZPAT1S(gpioz_base) = pin_mask;
				PZGID2LD(gpioz_base) = 1;
			}
		}else{
			PZINTC(gpioz_base) = pin_mask;
			PZMSKS(gpioz_base) = pin_mask;
			PZPAT1S(gpioz_base) = pin_mask;
			PZGID2LD(gpioz_base) = 1;
		}
	}

	if (munmap((void *)gpioz_base, 0x1000) < 0) //Z
        IMP_LOG_ERR(TAG, "munmap z failed\n");

	if (munmap((void *)gpio_base, 0x1000) < 0) //ABC
		IMP_LOG_ERR(TAG, "munmap failed\n");

    close(memfd);
	return 0;

}

static void *limit_t23_msc_ssi_gmac(void *args)
{
#define CPM0 0x10000020
#define CPM1 0x10000028

/*0x10000020 msc0 msc1 SSI0 bit4 bit5 bit6,*/
#define SSI0_CLK 0x40
#define MSC1_CLK 0x20
#define MSC0_CLK 0x10

/*0x10000028 gmac bit4,*/
#define GMAC_CLK 0x10

	int ret;
	int gpio_num;
	static uint32_t cpm_value = 0xffffffff;
	static uint32_t cpm1_value = 0xffffffff;
	
	while(1){
		ret = read_reg_32(CPM0, &cpm_value);
		ret = read_reg_32(CPM1, &cpm1_value);
		if(ret < 0) {
			IMP_LOG_ERR(TAG, "%s:%d get cpm error\n", __func__,__LINE__);
		}
		if((cpm_value & 0x70) < 0x70){
			if(!(cpm_value & SSI0_CLK))
				cpm_value = cpm_value | SSI0_CLK; /*gate clk ssi*/

			if(!(cpm_value & MSC1_CLK))
				cpm_value = cpm_value | MSC1_CLK; /*gate clk msc1*/

			if(!(cpm_value & MSC0_CLK))
				cpm_value = cpm_value | MSC0_CLK; /*gate clk msc0*/
			write_reg_32(CPM0, cpm_value);
		}
		if(!(cpm1_value & GMAC_CLK)){
			cpm1_value = cpm1_value | GMAC_CLK; /*gate clk gmac*/
			write_reg_32(CPM1, cpm1_value);
		}		
		
		set_gpio_input();

		sleep(1);
	}
}

static int zkey_check()
{
	int ret;
	uint32_t zkey, zkey_read;
	uint32_t chip_id_t23[3] = {0};

	ret = er_t23(CHIPID_BYTE_OFFSET/4, 3, chip_id_t23);
	ret = er_t23(ZKEY_BYTE_OFFSET/4, 1, (uint32_t *)&zkey_read);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "chip_verify soc failed\n");
		return -1;
	}
	gen_hash_32bit(chip_id_t23, CHIPID_SIZE, &zkey);

	if (zkey == zkey_read)
		return 0;

	return 1;
}

static int dkey_check()
{
	int ret;
	uint8_t dkey, dkey_read;
	uint32_t dkey_read_tmp;
	uint32_t chip_id_t23[3] = {0};

	ret = er_t23(CHIPID_BYTE_OFFSET/4, 3, chip_id_t23);
	ret = er_t23(DKEY_BYTE_OFFSET/4, 1, &dkey_read_tmp);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "chip_verify soc failed\n");
		return -1;
	}
	dkey_read = (dkey_read_tmp >> 16) & 0xff;

	dkey = crc32(0, chip_id_t23, 3);

	if(dkey == dkey_read)
		return 0;

	return 1;
}

static int MemTotal_check()
{
	int pos1, pos2;
    int sum = 0;
    FILE *f1 = fopen("/proc/cmdline", "r");
    char buff1[64];
    fscanf(f1, "%s", buff1);
    fscanf(f1, "%s", buff1);
    for (int i = 0; i < strlen(buff1); i++) {
		int sum1 = 0;
        if (buff1[i] == '=') {
            pos1 = i;
        }
        if (buff1[i] == 'M') {
            pos2 = i;
			for (int j = pos1 + 1; j < pos2; j++) {
				sum1 = sum1 * 10 + (buff1[j] - '0');
			}
        }
		sum += sum1;
		if(sum > 32){
			fclose(f1);
			return -1;
		}
    }
	fclose(f1);
	return 0;
}

int chip_verify(void)
{
	int ret,ret_d,arg;
	int id_value;
	uint32_t zkey, zkey_read;
	pthread_t tid_d;

	id_value = zrt_get_cpu_id();
    if (id_value == SOC_T23ZN || id_value == SOC_T23ZX) {
		ret = zkey_check();
		return ret;
    } else if (id_value == SOC_T23DN || id_value == SOC_T23DL) {
		if(id_value == SOC_T23DL){
			if(MemTotal_check() != 0){
				IMP_LOG_ERR(TAG, "This chip does not support DDR exceeding 32M\n");
				exit(-1);
			}
		}
		ret_d = dkey_check();
		if (ret_d == 0) {
			ret = pthread_create(&tid_d, NULL, limit_t23_msc_ssi_gmac, (void *)arg);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "create D pthread failed\n");
			}
		}
		return ret_d;
	} else {
		IMP_LOG_ERR(TAG, "zrt get_cpu_id error\n");
		return -1;
	}

	return 1;
}


#if 0
int chip_verify_old(void)
{
#define CHIPID_BYTE_OFFSET 0
#define CHIPID_SIZE 12
#define ZKEY_BYTE_OFFSET 52
#define ZKEY_SIZE 4
	int ret;
	uint32_t zkey, zkey_read;
	ret = zrt_get_cpu_id();
    if (ret == SOC_T23) {
		uint32_t chip_id_t30[3] = {0};
		ret = er_t30(CHIPID_BYTE_OFFSET, 3, chip_id_t30);
		ret = er_t30(ZKEY_BYTE_OFFSET/4, 1, (uint32_t *)&zkey_read);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "chip_verify soc failed\n");
			return -1;
		}
		gen_hash_32bit(chip_id_t30, CHIPID_SIZE, &zkey);
	} else {
		IMP_LOG_ERR(TAG, "zrt get_cpu_id error\n");
		return -1;
	}
	if (zkey == zkey_read)
		return 0;
	return 1;

}
#endif
